Skip to content

feat(git): Add Claude as a TextGenerationProvider#1323

Merged
juliusmarminge merged 23 commits intopingdotgg:mainfrom
keyzou:feat/claude-text-generation
Mar 25, 2026
Merged

feat(git): Add Claude as a TextGenerationProvider#1323
juliusmarminge merged 23 commits intopingdotgg:mainfrom
keyzou:feat/claude-text-generation

Conversation

@keyzou
Copy link
Contributor

@keyzou keyzou commented Mar 23, 2026

What Changed

  • Added Claude as an alternate provider for git operations
  • Added a dropdown in the settings to choose a provider for Git operations.
  • Default provider is Claude Haiku 4.5 (changeable), reasoning low (unchangeable)
  • Tried to keep the feature parity with Codex for git operations, hope I didn't miss anything

I suggest reviewing commit-by-commit :)

I'm also not too familiar with Effect, so there might be some AI slop here and there, don't hesitate to flag it

⚠️ I'm also proposing an optional last commit (c6c833e) to refactor both providers to extract the prompt building logic into shared utilities. This is fairly opinionated, and we should probably discuss this, so I'm totally down to just drop that commit; we might want to tweak the prompts for a specific provider in the future

⚠️ I also couldn't test for regression on the Codex side, so I'll ask for one of you to make sure this all works correctly (tests are passing, I added a few for Claude, but maybe some bug went through, especially with the refactor)

I saw a similar PR at #1222 but since the change was not enough, I wanted to take a stab at the issue myself (not sure about the proper open-source etiquette here though)

Fixes #1221

Why

Pretty straightforward: I don't have Codex, so Git-related operations couldn't work for me :)

UI Changes

Here's the setting page with the Git section using Claude:

Enregistrement.de.l.ecran.2026-03-24.a.09.03.52.mov

Here's a demo of the branch name generation working (not sure I have the right flow) -- old model picker, see the video above for the updated version ^:

Enregistrement.de.l.ecran.2026-03-23.a.02.17.41.mp4

And here's a demo of committing and pushing (thus generating a commit message) -- old model picker, see the video above for the updated version:

Enregistrement.de.l.ecran.2026-03-23.a.02.15.14.mp4

Checklist

  • This PR is small and focused (not that small actually)
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Add Claude as a TextGenerationProvider for git writing alongside Codex

  • Introduces ClaudeTextGenerationLive in ClaudeTextGeneration.ts that invokes the claude CLI with structured JSON output, forwarding model options (effort, thinking, fastMode) as CLI flags.
  • Adds RoutingTextGenerationLive in RoutingTextGeneration.ts that dispatches to Claude or Codex based on modelSelection.provider; the server runtime now uses this routing layer instead of CodexTextGenerationLive directly.
  • Replaces the flat textGenerationModel string with a structured ModelSelection object (provider + model + options) throughout the contracts, API, settings schema, and all text generation service interfaces.
  • Adds a provider-and-model picker (with traits/options) to the settings UI for the "Git writing model" field, backed by resolveAppModelSelectionState in modelSelection.ts.
  • Extracts shared prompt builders and utilities into Prompts.ts and Utils.ts, used by both Codex and Claude implementations.
  • Risk: GitRunStackedActionInput now requires modelSelection and drops textGenerationModel; any client not sending modelSelection will fail schema validation.

Macroscope summarized 83a3dbd.


Note

Medium Risk
Introduces a new text-generation backend and changes the git RPC contract to require modelSelection, so older clients or incorrect provider/options mapping could break git commit/PR generation.

Overview
Adds Claude-backed git text generation via claude -p with schema-validated structured JSON output, plus a new RoutingTextGenerationLive layer that dispatches requests between Codex and Claude based on modelSelection.provider.

Reworks the git-writing API to pass a structured modelSelection (provider/model/options) end-to-end: contracts (GitRunStackedActionInput), TextGeneration service inputs, GitManager plumbing, websocket/native API calls, and the web UI settings. The settings page now lets users choose provider+model and adjust supported traits, and shared prompt/error/schema utilities are extracted into new git/Prompts.ts and git/Utils.ts (with corresponding tests).

Written by Cursor Bugbot for commit 83a3dbd. This will update automatically on new commits. Configure here.

@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c98ac01f-5ef6-4a49-978f-864a092dc864

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 23, 2026
@juliusmarminge
Copy link
Member

can we reuse the model picker from the chat UI? So it's a single select with provider top level, model submenu?

@keyzou
Copy link
Contributor Author

keyzou commented Mar 23, 2026

yep, didn't think about that, what do you think ? (updated the screenshot in the PR aswell)

Enregistrement.de.l.ecran.2026-03-23.a.19.37.28.mov

@juliusmarminge
Copy link
Member

looks good. resolve conflicts pls

@keyzou keyzou force-pushed the feat/claude-text-generation branch from c9d1242 to 66463d1 Compare March 24, 2026 08:02
@github-actions github-actions bot added size:XL 500-999 changed lines (additions + deletions). and removed size:XXL 1,000+ changed lines (additions + deletions). labels Mar 24, 2026
@keyzou
Copy link
Contributor Author

keyzou commented Mar 24, 2026

done, rebased on main (updated the video to reflect the recent UI changes on main)

"--effort",
CLAUDE_REASONING_EFFORT,
"--dangerously-skip-permissions",
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude CLI JSON schema may break on Windows

Medium Severity

The Claude CLI receives the JSON schema as an inline command-line argument via --json-schema jsonSchemaStr, while on Windows shell is set to true. The JSON string contains double quotes and braces that cmd.exe may interpret or mangle. The Codex implementation avoids this by writing the schema to a temp file and passing the file path instead.

Fix in Cursor Fix in Web

Copy link
Contributor Author

@keyzou keyzou Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be dismissed: the schema should always be simple enough to never cause issues (for now), there should never be any special character outside the quotes and braces from the json in this case? which should already be properly handled by cmd.exe

@keyzou keyzou force-pushed the feat/claude-text-generation branch from dbe7e77 to cb76bce Compare March 24, 2026 09:20
@keyzou
Copy link
Contributor Author

keyzou commented Mar 24, 2026

Left a few items from bugbot, should be fine if left as-is but leaving the last call to the maintainers

@keyzou keyzou force-pushed the feat/claude-text-generation branch from cb76bce to e30f4b6 Compare March 24, 2026 09:32
keyzou and others added 13 commits March 24, 2026 18:56
Move limitSection, sanitizeCommitSubject, and sanitizePrTitle into a
dedicated textGenerationUtils module so they can be reused by multiple
text generation providers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the single DEFAULT_GIT_TEXT_GENERATION_MODEL constant with a
DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER map keyed by provider kind,
enabling per-provider default models. Update all consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nterfaces

Introduce a standalone TextGenerationProvider union type and add an
optional provider field to all text generation input interfaces, allowing
callers to select which backend generates the content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ClaudeTextGeneration layer that spawns `claude -p` with structured
JSON output for commit messages, PR content, and branch names. Introduce
a RoutingTextGeneration layer that dispatches to Codex or Claude based
on the provider field, and wire it into the server DI graph. Thread the
provider selection through GitManager to all text generation call sites.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a provider selector (Codex/Claude) to the git settings panel with
dynamic model options per provider. Pass the selected provider through
to the stacked action mutation. Also fix a pre-existing bug where the
model field name was incorrect in the RPC call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match the pattern already used by Codex (CODEX_REASONING_EFFORT)
to make the setting visible and easy to change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract prompt construction logic into buildCommitMessagePrompt, buildPrContentPrompt, and buildBranchNamePrompt in new textGenerationPrompts.ts
- Replace duplicate normalizeClaudeError and normalizeCodexError with shared normalizeCliError function parameterized by CLI name
- Update Claude and Codex text generation layers to use shared utilities, reducing duplication
- Add comprehensive tests for prompt builders and error normalization
- Consolidate provider and model selection into reusable component
- Replace getAppModelOptions with getCustomModelOptionsByProvider
- Simplifies settings UI and enables reuse across the app
Remove explicit result interfaces with incorrect type annotations
(Schema.Struct.Type<any>) and let TypeScript infer return types.
Split the ternary schema assignment into separate return branches
so TypeScript preserves a proper union instead of widening to any.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both ClaudeTextGeneration and CodexTextGeneration had identical logic
to convert an Effect Schema into a flat JSON Schema object. Move it
into textGenerationUtils.ts as a single shared helper.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass selectedModel (scoped to the active provider) through to
getAppModelOptions so non-built-in models appear in the dropdown.
Restore the per-setting reset button on the git writing model row.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pass input.textGenerationProvider to runFeatureBranchStep so the
routing layer respects the user's provider choice instead of always
falling back to Codex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@keyzou keyzou force-pushed the feat/claude-text-generation branch from 20a38c2 to b141993 Compare March 24, 2026 18:00
@keyzou
Copy link
Contributor Author

keyzou commented Mar 25, 2026

@juliusmarminge one last check with the stuff bugbot brought up ? :)

@github-actions github-actions bot added size:XXL 1,000+ changed lines (additions + deletions). and removed size:XL 500-999 changed lines (additions + deletions). labels Mar 25, 2026
- update git contract test to include provider model selection
- cover the new `modelSelection` field in stacked action input
- Include `modelSelection` in wsServer git action cases
- Keep test fixtures aligned with Claude text generation flow
- Default git text generation to low reasoning effort
- Align test coverage with the new Codex config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low

const cleanup = Effect.all(
[schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)),
{
concurrency: "unbounded",
},
).pipe(Effect.asVoid);
return yield* Effect.gen(function* () {
yield* runCodexCommand.pipe(
Effect.scoped,
Effect.timeoutOption(CODEX_TIMEOUT_MS),
Effect.flatMap(
Option.match({
onNone: () =>
Effect.fail(
new TextGenerationError({ operation, detail: "Codex CLI request timed out." }),
),
onSome: () => Effect.void,
}),
),
);
return yield* fileSystem.readFileString(outputPath).pipe(
Effect.mapError(
(cause) =>
new TextGenerationError({
operation,
detail: "Failed to read Codex output file.",
cause,
}),
),
Effect.flatMap(Schema.decodeEffect(Schema.fromJsonString(outputSchemaJson))),
Effect.catchTag("SchemaError", (cause) =>
Effect.fail(
new TextGenerationError({
operation,
detail: "Codex returned invalid structured output.",
cause,
}),
),
),
);
}).pipe(Effect.ensuring(cleanup));

Temp files created with makeTempFileScoped already have automatic cleanup finalizers registered to the scope, but lines 211–216 also schedule manual cleanup via Effect.ensuring(cleanup). This results in double deletion attempts — once from scoped finalizers and once from the ensuring block. While safeUnlink suppresses the resulting errors, the redundant cleanup suggests the scoped migration was incomplete. Consider removing the manual cleanup and relying on makeTempFileScoped's automatic finalizers.

-      const cleanup = Effect.all(
-        [schemaPath, outputPath, ...cleanupPaths].map((filePath) => safeUnlink(filePath)),
-        {
-          concurrency: "unbounded",
-        },
-      ).pipe(Effect.asVoid);
-
       return yield* Effect.gen(function* () {
         yield* runCodexCommand.pipe(
           Effect.scoped,
@@ -250,7 +244,7 @@
             ),
           ),
         );
-      }).pipe(Effect.ensuring(cleanup));
+      }).pipe(Effect.scoped);
     });
 };
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/git/Layers/CodexTextGeneration.ts around lines 211-253:

Temp files created with `makeTempFileScoped` already have automatic cleanup finalizers registered to the scope, but lines 211–216 also schedule manual cleanup via `Effect.ensuring(cleanup)`. This results in double deletion attempts — once from scoped finalizers and once from the ensuring block. While `safeUnlink` suppresses the resulting errors, the redundant cleanup suggests the scoped migration was incomplete. Consider removing the manual cleanup and relying on `makeTempFileScoped`'s automatic finalizers.

Evidence trail:
apps/server/src/git/Layers/CodexTextGeneration.ts lines 58-79 (writeTempFile definition using makeTempFileScoped, safeUnlink definition), lines 133-139 (schemaPath and outputPath creation), lines 211-217 (cleanup effect definition), line 251 (Effect.ensuring(cleanup) application). The return type at line 62 shows Scope.Scope requirement confirming scoped resource pattern.

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Effect.mapError((cause) =>
normalizeCliError("claude", operation, cause, "Failed to collect process output"),
),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate readStreamAsString helper across both providers

Low Severity

The readStreamAsString helper is defined identically in both ClaudeTextGeneration.ts and CodexTextGeneration.ts, differing only in the CLI name string passed to normalizeCliError. Given that other shared logic (prompts, sanitizers, normalizeCliError, toJsonSchemaObject) was extracted to Utils.ts and Prompts.ts, this helper could also be parameterized by CLI name and shared from Utils.ts.

Additional Locations (1)
Fix in Cursor Fix in Web

@juliusmarminge juliusmarminge merged commit ff8d87d into pingdotgg:main Mar 25, 2026
11 checks passed
emrezeytin pushed a commit to emrezeytin/t3code that referenced this pull request Mar 25, 2026
Co-authored-by: keyzou <keyzou@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
@DragonSenseiGuy
Copy link

sob i wish this was a part of 0.0.14

aaditagrawal pushed a commit to aaditagrawal/t3code that referenced this pull request Mar 26, 2026
Co-authored-by: keyzou <keyzou@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Julius Marminge <julius0216@outlook.com>
bulbulogludemir added a commit to bulbulogludemir/krabbycode that referenced this pull request Mar 26, 2026
…ings architecture

Major upstream changes merged:
- Server-authoritative settings (pingdotgg#1421): appSettings.ts deleted, settings
  moved to serverSettings.ts + contracts/settings.ts + useSettings hook
- Provider-aware model selections (pingdotgg#1371): model→modelSelection refactor,
  new composerDraftStore, unified TraitsPicker, migration 016
- Context window usage UI (pingdotgg#1351): ContextWindowMeter component
- Claude as TextGenerationProvider (pingdotgg#1323): git text generation via Claude
- Bootstrap FD for security (pingdotgg#1398): sensitive config sent over file descriptor
- Sidebar recency sorting (pingdotgg#1372): projects/threads sorted by recency
- ProviderHealth replaced by ProviderRegistry + per-provider services
- Word wrapping, terminal history preservation, various fixes

Krabby-specific adaptations:
- Migration 016 renumbered to 024 (Krabby 016-023 preserved)
- 20+ Krabby settings ported to new ClientSettingsSchema
- KrabbyProviderOptions schema for chrome/enterprise/budget fields
- All @t3tools@kRaBby, t3code→krabbycode, service keys t3/→krabby/

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Allow Claude to generate commit messages

3 participants